home *** CD-ROM | disk | FTP | other *** search
/ 8bitfiles.net/archives / archives.tar / archives / genie-commodore-file-library / Information / BCD.PART2.ARC / BCD PART II
Encoding:
Text File  |  2019-04-13  |  29.8 KB  |  692 lines

  1. *********************************************************************
  2. This article is being presented through the *StarBoard* Journal of
  3. the FlagShip/StarShip, SIGS (Special Interest Groups) on the
  4. Delphi and GEnie telecommunications networks.  Permission is
  5. hereby granted to non-profit organizations only to reprint this
  6. article or pass it along electronically as long as proper credit
  7. is given to both the author and the *StarBoard* Journal.
  8. *********************************************************************
  9.  
  10.                    THE ABC's OF BCD (part 2)
  11.  
  12.         Manipulating Decimal Numbers In Machine Language
  13.  
  14.                          by W.J. Brier
  15.  
  16.                     (TROUBLESOME on DELPHI)
  17.  
  18. ===========================================================================
  19. INTRODUCTION
  20.  
  21. If you have thoroughly read THE ABC'S OF BCD part 1 and have tried out the
  22. program examples that were presented, you should have an elementary
  23. understanding of binary coded decimal (BCD) numbers.  If some of the
  24. concepts presented in part 1 are not clear, I suggest that you read over
  25. the material again and try out the program examples before continuing on
  26. with part 2 of this series.
  27.  
  28. In part 1 you were presented with the basic concept of binary coded decimal
  29. numbers and were shown how to store such numbers.  Routines were presented
  30. that illustrated the techniques of encoding a two digit ASCII number into a
  31. single BCD digit and decoding a single BCD digit back to ASCII numerals.
  32. With this information safely tucked away in your mind, let's move onward to
  33. new ground.
  34.  
  35. In THE ABC'S OF BCD part 2, methods of encoding and decoding multi-digit
  36. numbers will be discussed.  Also, the technique of stripping leading and
  37. trailing zeros, and the right justification of dollars and cents numbers,
  38. will be explored.  Program examples will continue to use the same symbology
  39. used in part 1 of the series.
  40.  
  41. I.  ENCODING ASCII DECIMAL STRINGS TO BCD NUMBERS
  42.  
  43. Before any type of mathematical operation can be performed on an ASCII
  44. number, it must be encoded into a form suitable for use by the computer.
  45. The BASIC interpreter, you may recall, changes ASCII decimal numbers into
  46. five byte binary format.  This process occasionally introduces errors that
  47. are the natural result of converting from one number base (base 10) to
  48. another (base 2).  However, the system being discussed here is a decimal
  49. system and will not result in conversion errors.
  50.  
  51. The first step in encoding an ASCII decimal string into a BCD number is to
  52. format the string in such a manner that it always is a standard length,
  53. with the decimal point in fixed position.  This requires that the string be
  54. scanned to find its length and decimal point position.  Once these two
  55. items have been determined the string must be padded with zeros if it is
  56. shorter than the standard length required by the encoding program.  This
  57. process is referred to as NORMALIZING.
  58.  
  59. Only after normalizing has been performed can BCD encoding be accomplished.
  60. Although it will not be discussed at this time, means should be developed
  61. to prevent an overflow condition due to an excessively large ASCII number
  62. being input into the program.  An overflow will result in a grossly
  63. inaccurate BCD number due to truncating of the highest digits.  If the
  64. number has too many places to the right of the decimal point, the excess
  65. will also be truncated, resulting in some additional inaccuracy.  To some
  66. extent this can be avoided by testing the length of the ASCII input string
  67. and making appropriate corrections.
  68.  
  69. Before presenting an example of how to encode an ASCII number into BCD, I'd
  70. like to discuss what is meant by normalizing the ASCII string.  Let's
  71. suppose that the user types in the ASCII floating point number:
  72.  
  73.     256.781
  74.  
  75. Let's further suppose that the ASCII to BCD encoding algorithm allows four
  76. places to the right of the decimal point and six places to the left.
  77. Formatting the above input to the normalized format imposed by these place
  78. limits results in:
  79.  
  80.     000256.7810
  81.  
  82. As can be seen, the string has been padded with ASCII zeros to fill in the
  83. unused places on either side of the decimal point.  The original input has
  84. been change to the normalized format.  Let's look at a few more examples,
  85. using the place limits imposed above, so as to better understand this
  86. process:
  87.  
  88.     INPUT                   NORMALIZED
  89.     ===================================
  90.     100                     000100.0000
  91.     .0004                   000000.0004
  92.     21.786                  000021.7860
  93.     423098.45238            423098.4523
  94.     2345.28                 002345.2800
  95.     0.002307                000000.0023
  96.     ===================================
  97.  
  98. It is apparent from the fourth and sixth examples that excess digits to the
  99. right of the decimal point are simply lost.  The resulting BCD number will,
  100. of course, be slightly inaccurate due to this truncation.  It is also
  101. apparent that the decimal point is no longer necessary to represent the
  102. number.  Because the string has been changed to the normalized format, the
  103. decimal point is now understood to be between the sixth and seventh digits
  104. of the string and thus may be ignored.
  105.  
  106. As the normalized format in this case is limited to ten digits total, the
  107. user input should likewise be restricted to a maximum of ten digits and one
  108. decimal point.  An input format checking algorithm should be used to filter
  109. out strings that fail to conform to these limits or that have non-numeric
  110. characters.
  111.  
  112. Ignoring the decimal point reduces the input to a normalized format of ten
  113. ASCII digits, with zeros padding the unused positions.  The above table in
  114. this format will look like this:
  115.  
  116.     INPUT                    NORMALIZED
  117.     ===================================
  118.     100                      0001000000
  119.     .0004                    0000000004
  120.     21.786                   0000217860
  121.     423098.45238             4230984523
  122.     2345.28                  0023452800
  123.     0.002307                 0000000023
  124.     ===================================
  125.  
  126. With the ASCII decimal string normalized, the encoding process is greatly
  127. simplified.  The actual procedure for normalizing will now be discussed.
  128.  
  129. As with encoding a pair of ASCII digits into a single BCD digit, a logical
  130. procedure should be followed.  It is assumed that the ASCII number to be
  131. encoded is in a storage area that will be referred to as BUF (for input
  132. buffer) and that the string is terminated by a null ($00):
  133.  
  134.     1.  Clear the ASCII accumulator (ACUMA) by filling it with ASCII
  135.     zeros;
  136.  
  137.     2.  Scan the ASCII input string to locate the decimal point (if
  138.     any);
  139.  
  140.     3.  Starting at the digit immediately to the left of the decimal
  141.     point location, transfer digits from BUF to ACUMA in descending
  142.     order;
  143.  
  144.     4.  Starting at the digit immediately to the right of the decimal
  145.     point location, transfer digits from BUF to ACUMA in ascending
  146.     order.
  147.  
  148. That essentially is all there is to it.  A suitable algorithm to accomplish
  149. this will now be presented.
  150.  
  151. This algorithm will be structured as a subroutine and will be referred to
  152. with the label FRMAT (the routine does not check for a negative number):
  153.  
  154.     FRMAT    LDA #$48       ;ASCII ZERO
  155.              LDX #$09       ;ACUMA OFFSET
  156.                             ;
  157.     FRMAT1   STA ACUMA,X    ;FILL ACCUMULATOR WITH ZEROS
  158.              DEX
  159.              BPL FRMAT1     ;LOOP
  160.                             ;
  161.              LDX #$00       ;USE .X AS BUF OFFSET
  162.                             ;
  163.     FRMAT2   LDA BUF,X      ;FETCH INPUT BYTE
  164.              BEQ FRMAT3     ;END OF STRING
  165.                             ;
  166.              CMP #$2E       ;ASCII DECIMAL POINT
  167.              BEQ FRMAT3     ;DECIMAL POINT FOUND
  168.                             ;
  169.              INX            ;NEXT CHARACTER
  170.              BNE FRMAT2     ;LOOP
  171.                             ;
  172.     FRMAT3   TXA            ;SAVE LOCATION IN BUF...
  173.              PHA            ;ON STACK
  174.              DEX            ;SKIP DECIMAL POINT OR NULL
  175.                             ;
  176.              LDY #$05       ;ACUMA OFFSET
  177.                             ;
  178.     FRMAT4   LDA BUF,X      ;FETCH ASCII DIGIT
  179.              STA ACUMA,Y    ;STORE IN ACCUMULATOR
  180.              DEY            ;CHANGE ACUMA OFFSET
  181.              DEX            ;CHANGE BUF OFFSET
  182.              BPL FRMAT4     ;LOOP
  183.                             ;
  184.              PLA            ;FETCH DECIMAL/NULL LOCATION
  185.              TAX            ;BECOMES BUF OFFSET
  186.              LDY #$06       ;ACUMA OFFSET
  187.                             ;
  188.     FRMAT5   LDA BUF,X      ;FETCH ASCII DIGIT
  189.              BEQ FRMATO     ;END OF INPUT STRING
  190.                             ;
  191.              CMP #$2E       ;DECIMAL POINT?
  192.              BEQ FRMAT6     ;YES, SKIP
  193.                             ;
  194.              STA ACUMA,Y    ;STORE DIGIT
  195.              INY            ;NEW ACUMA OFFSET
  196.                             ;
  197.     FRMAT6   INX            ;NEW BUF OFFSET
  198.              BNE FRMAT5     ;LOOP
  199.                             ;
  200.     FRMATO   RTS            ;EXIT
  201.  
  202. Upon entering this subroutine the first operation performed is fill the
  203. ASCII accumulator (ACUMA) with ASCII zeros.  Then the input string in BUF
  204. is scanned (starting at the loop at FRMAT2) until the decimal point has
  205. been located, using the .X register as an offset.  If the end of the input
  206. string is reached (which would be detected by loading the terminating
  207. null), the scan is aborted.  Otherwise, looping continues until the decimal
  208. point is found at which time the scan is terminated.  The current BUF
  209. offset in .X is saved on the stack.
  210.  
  211. Next, the ASCII digits starting to the left of the decimal point in BUF are
  212. transferred to ACUMA via the loop set up at FRMAT4.  Note that the ACUMA
  213. offset in the .Y register starts by pointing at the digit immediately to
  214. the left of the assumed decimal point location in ACUMA.  Upon completion
  215. of this transfer, the decimal point location in BUF is pulled from the
  216. stack and now digits to the right of the decimal point are moved to ACUMA,
  217. via the loop at FRMAT5.  The loop terminates when the null at the end of
  218. BUF is reached, indicating that there are no more characters to transfer.
  219.  
  220. Because ACUMA was filled with zeros before any transferring was performed,
  221. any unused locations in ACUMA will still contain zeros and thus will result
  222. in the ASCII string being properly padded.  Here is what ACUMA would look
  223. like at each of the three major points in the routine.  It is assumed that
  224. the ASCII input string 1371.08 is being formatted:
  225.  
  226.     LABEL    ACUMA
  227.     ===================
  228.     FRMAT2   0000000000
  229.     FRMAT5   0013710000
  230.     FRMATO   0013710800
  231.     ===================
  232.  
  233. It is apparent from the foregoing that normalizing permits the entry of
  234. floating point decimal numbers, as the actual location of the decimal point
  235. is determined during the formatting operation.  The result however is
  236. always a string in normalized format.  With the ASCII number properly
  237. formatted, ASCII to BCD conversion may now be performed.
  238.  
  239. As you may recall, in THE ABC'S OF BCD part 1 a subroutine (ASBCD) was
  240. presented that would convert two ASCII digits into a single BCD digit.  By
  241. using ASBCD within a loop it is possible to encode the entire number
  242. contained in ACUMA, the result being deposited in BCD accumulator ACUM1.
  243. This is accomplished by successively fetching characters from the ASCII
  244. string, calling the conversion subroutine and storing the resulting BCD
  245. digit in ACUM1.  Two offsets are required for this purpose, one to keep
  246. track of the current location within ACUMA and the other to maintain
  247. position in ACUM1.
  248.  
  249. In the formatting example presented above, a normalized ASCII number is ten
  250. digits, six to the left of the decimal point and four to the right.  The
  251. arrangement of the BCD accumulators obviously must reflect this format, as
  252. there is a two-for-one relationship between the ASCII string and its BCD
  253. equivalent.  Thus ACUM1 and ACUM2, in this case, would be five bytes in
  254. length, with the decimal point assumed to be between the third and fourth
  255. byte.
  256.  
  257. Here is an algorithm to convert the normalized ASCII number in ACUMA to a
  258. BCD number in ACUM1 (using the above value).  This subroutine will be
  259. labeled ENCD:
  260.  
  261.     ENCD     LDY #0         ;ACUMA OFFSET
  262.              STY SFLG1      ;ACUM1 OFFSET
  263.                             ;
  264.     ENCD1    LDA ACUMA,Y    ;FETCH TENS DIGIT
  265.              TAX            ;GIVE TO .X REGISTER
  266.              INY            ;NEXT DIGIT
  267.              LDA ACUMA,Y    ;FETCH UNITS DIGIT
  268.              JSR ASBCD      ;CALL CONVERSION ROUTINE
  269.                             ;
  270.              LDX SFLG1      ;FETCH ACUM1 OFFSET
  271.              STA ACUM1,X    ;SAVE BCD DIGIT
  272.                             ;
  273.              INC SFLG1      ;BUMP ACUM1 OFFSET
  274.                             ;
  275.              INY            ;BUMP ACUMA OFFSET
  276.              CPY #$0A       ;END OF ACUMA?
  277.              BNE ENCD1      ;NO, SO LOOP
  278.                             ;
  279.              RTS            ;EXIT
  280.  
  281. Upon exit from this routine the ASCII number in ACUMA will be in BCD format
  282. in ACUM1 (ACUMA is left undisturbed).  For the purposes of conversion each
  283. pair of ASCII digits fetched from ACUMA is treated as a units and tens pair
  284. by ASBCD.  The .Y register keeps track of the position within ACUMA (.Y is
  285. not disturbed by ASBCD).  The ACUM1 offset has to be kept in RAM as the .X
  286. register is used by the ASBCD subroutine.  Assuming that the ASCII number
  287. 1371.08 is being encoded, here are the stages that ACUM1 will go through
  288. during each iteration of the routine (ACUM1 values are in hex):
  289.  
  290.     CYCLE    ACUM1
  291.     =======================
  292.     Start    ** ** ** ** **
  293.       1      00 ** ** ** **
  294.       2      00 13 ** ** **
  295.       3      00 13 71 ** **
  296.       4      00 13 71 08 **
  297.       5      00 13 71 08 00
  298.     =======================
  299.  
  300. The asterisks indicate random values that may be in ACUM1 from a previous
  301. operation.
  302.  
  303. To summarize this chapter, converting an ASCII decimal number requires that
  304. it be normalized and then encoded two digits at a time into a series of
  305. single BCD digits, these digits being stored in the BCD accumulator.  With
  306. a basic understanding of this process, let's go on to the next step,
  307. converting a BCD number back to an ASCII decimal string.
  308.  
  309. II.  DECODING BCD NUMBERS TO ASCII DECIMAL STRINGS
  310.  
  311. Before a BCD number can be displayed to the user it must be decoded and the
  312. resulting string of ASCII digits must be formatted in a manner suitable for
  313. use by the display routine.  Formatted ASCII strings may also be JUSTIFIED
  314. so that all decimal points align in a columnar display.  Justification is
  315. useful for displaying columns of figures and lends a professional
  316. appearance to the display.  In some cases however, justification may not be
  317. desirable.
  318.  
  319. BCD to ASCII conversion requires no special preparation of the BCD number,
  320. unless it is negative (which may be the result of some mathematical
  321. operation).  If the number is negative then the sign flag for the
  322. accumulator must be set to indicate this fact and the sign bit in the first
  323. BCD digit cleared.
  324.  
  325. Once the sign of the BCD number has been established, a loop is set up to
  326. successively decode each BCD digit to its corresponding ASCII values, these
  327. values being deposited in the ASCII accumulator (ACUMA).  As with the
  328. encoding routine presented above, two offsets are required to maintain
  329. position in both the BCD and ASCII accumulators.  The examples to be
  330. presented will assume that the number to decode is in the BCD accumulator
  331. (ACUM1).  The ASCII equivalent will be stored in ACUMA.  Part of the
  332. decoding routine of course, is to insert a decimal point at the correct
  333. location in the ASCII string.
  334.  
  335. The decoding routine to be presented always places the decimal point at the
  336. same location in the ASCII accumulator.  Let's first look at how some BCD
  337. numbers would appear after decoding, but before any additional formatting
  338. (accumulator sizes are as they were in the previous examples on encoding to
  339. BCD):
  340.  
  341.     BCD Number              ASCII String
  342.     =====================================
  343.     00 00 00 00 00           000000.0000*
  344.     00 13 71 08 00           001371.0800*
  345.     71 00 61 00 03           710061.0003*
  346.     00 00 00 00 77           000000.0077*
  347.     80 04 29 03 11           -00429.0311*
  348.     82 92 03 04 00           -29203.0400*
  349.     =====================================
  350.  
  351. The asterisks represent the terminating null ($00) at the end of each ASCII
  352. string.
  353.  
  354. In examining these numbers a distinct set of patterns emerge.  The decimal
  355. point is always the seventh character in the string and when the BCD number
  356. is negative the minus sign (-) replaces the digit in the first character.
  357. The string is padded with zeros.  Formatting would be used to remove the
  358. leading and/or trailing zeros if they are not desired in the display.
  359.  
  360. Here is an algorithm to decode the BCD number contained in BCD accumulator
  361. ACUM1:
  362.  
  363.     DECD     LDX #$00
  364.              LDY #$00       ;ACUMA OFFSET
  365.              STX SFLG1      ;CLEAR SIGN FLAG
  366.              STX ACUMA+11   ;ASCII STRING TERMINATOR
  367.                             ;
  368.              LDA ACUM1      ;CHECK SIGN OF BCD NUMBER
  369.              BPL DECD1      ;POSITIVE NUMBER
  370.                             ;
  371.              DEC SFLG1      ;SET SIGN FLAG TO NEGATIVE
  372.              AND #$7F       ;MASK SIGN BIT
  373.              STA ACUM1      ;SAVE IN ACCUMULATOR
  374.                             ;
  375.     DECD1    STX SFLG2      ;ACUM1 OFFSET
  376.                             ;
  377.              LDA ACUM1,X    ;FETCH BCD DIGIT
  378.              JSR BCDAS      ;CALL DIGIT DECODING ROUTINE
  379.                             ;
  380.              STA ACUMA,Y    ;SAVE TENS DIGIT
  381.              INY            ;BUMP OFFSET
  382.              TXA
  383.              STA ACUMA,Y    ;SAVE UNITS
  384.              INY            ;BUMP OFFSET
  385.              CPY #$06       ;TIME FOR DECIMAL POINT?
  386.              BNE DECD2      ;NO
  387.                             ;
  388.              LDA #$2E       ;ASCII DECIMAL POINT
  389.              STA ACUMA,Y    ;STORE
  390.              INY            ;BUMP OFFSET
  391.                             ;
  392.     DECD2    LDX SFLG2      ;ACUM1 OFFSET
  393.              INX
  394.              CPX #$05       ;FINISHED?
  395.              BNE DECD1      ;NO, SO LOOP
  396.                             ;
  397.              BIT SFLG1      ;CHECK SIGN
  398.              BPL DECDO      ;POSITIVE NUMBER
  399.                             ;
  400.              LDA #$2D       ;ASCII MINUS SIGN
  401.              STA ACUMA      ;STORE
  402.                             ;
  403.     DECDO    RTS            ;EXIT
  404.  
  405. Upon exit from this routine, ACUMA will contain the ASCII string
  406. corresponding to the BCD number in ACUM1.
  407.  
  408. The first character of ACUMA will contain a minus sign if the BCD number
  409. was negative.  The routine loops five times to accomplish its task.  Here
  410. are the successive stages that ACUMA would go through for each iteration
  411. (ACUM1 is assumed to contain $00 $13 $78 $08 $00):
  412.  
  413.     ITERATION  ACUMA
  414.     ======================
  415.     Start      ***********
  416.       1        00*********
  417.       2        0013*******
  418.       3        001378.****
  419.       4        001378.08**
  420.       5        001378.0800
  421.     ======================
  422.  
  423. In this example the asterisks represent random values that may be in the
  424. ASCII accumulator from a previous operation.  Note that when the value in
  425. the .Y register reaches six the routine inserts a decimal point into ACUMA
  426. and increments .Y so that the next digit will follow the decimal point.
  427. The BCDAS routine was presented in part 1 of this series and illustrated
  428. the technique of decoding a BCD digit.  You may wish to refer to that
  429. routine if you aren't sure as to where the values came from.
  430.  
  431. The first step of the decoding process is to determine the sign of the BCD
  432. number.  As you may recall, the seventh bit of the first BCD digit is set
  433. if the number is negative.  Thus, loading the .A register with the first
  434. digit either sets Ar clears the N flag in the CPU status register.  The
  435. routine uses that fact to determine sign and set the sign flag SFLG1 if the
  436. BCD number is negative.  Before actually decoding a negative number, the
  437. seventh bit must be cleared via use of the AND operation to avoid an
  438. erroneous result from BCDAS.  Here is an example of how this would operate:
  439.  
  440.     .A Register      1000 0110 = $86
  441.     AND #$7F         0111 1111 = $7F
  442.     ================================
  443.     .A Register      0000 0110 = $06
  444.  
  445. With the sign taken care of, the BCD digits are fetched one at a time,
  446. decoded and the resulting ASCII digits are stored in ACUMA.  The offsets
  447. are then incremented accordingly, the ACUM1 offset also acting as a loop or
  448. iteration counter.  Since there are five BCD digits to decode five
  449. iterations are performed.  After the last iteration, the sign flag SFLG1 is
  450. examined with the BIT instruction.  If the decoded BCD number is negative
  451. the routine simply places a minus sign into the first byte of ACUMA.
  452. Otherwise, the byte is not disturbed.
  453.  
  454. At this point ACUMA contains an ASCII string which may be padded with
  455. leading and trailing zeros.  Prior to actually displaying the number, it
  456. may be desirable to perform some additional formatting on the string to
  457. modify its appearance.
  458.  
  459. III.  FORMATTING ASCII DECIMAL STRINGS
  460.  
  461. When displaying numbers on the screen or printer it is often desirable to
  462. have them conform to a certain appearance.  For example, you may want
  463. trailing zeros and decimal points aligned (justified) or you may want all
  464. numbers to start at the same point on the screen or page.  To accomplish
  465. these goals some type of formatting is usually required.
  466.  
  467. As decoded, the string in ACUMA has the decimal point in the seventh
  468. character position and may have leading and/or trailing zeros, depending on
  469. the BCD number that was decoded.  Because the decimal point is always in
  470. the same location (fixed) the string may be considered justified as it
  471. sits.  In the majority of the cases, you will want to strip the leading
  472. zeros from the string for display.  That can be accomplished by replacing
  473. leading zeros with blanks (ASCII 32) from within a loop.  When the first
  474. non-zero character is found, the loop is terminated.  The procedure is
  475. slightly complicated by the possibility of a minus sign being found in the
  476. first character position.
  477.  
  478. Here is an algorithm to strip the leading zeros from an ASCII decimal
  479. string in ACUMA:
  480.  
  481. LDSTP    LDY #0         ;OFFSET
  482.                         ;
  483. LDSTP1   LDA ACUMA,Y    ;FETCH CHARACTER
  484.          CMP #$2D       ;ASCII MINUS SIGN
  485.          BEQ LDSTP2     ;SKIP
  486.                         ;
  487.          CMP #$2E       ;ASCII DECIMAL POINT
  488.          BEQ LDSTPO     ;TERMINATE LOOP
  489.                         ;
  490.          CMP #$30       ;ASCII ZERO
  491.          BNE LDSTPO     ;TERMINATE LOOP
  492.                         ;
  493.          LDA #$20       ;ASCII BLANK
  494.          STA ACUMA,Y    ;REPLACE LEADING ZERO
  495.                         ;
  496. LDSTP2   INY            ;BUMP OFFSET
  497.          BNE LDSTP1     ;LOOP
  498.                         ;
  499. LDSTPO   RTS            ;EXIT
  500.  
  501. The routine requires up to 6 iterations to complete this operation, the
  502. actual number of iterations depending on the number of leading zeros found.
  503.  
  504. Understanding how it works is best accomplished by looking at how a string
  505. is changed in each iteration:
  506.  
  507.     iteration    ACUMA
  508.     =====================
  509.     Start    -00045.8600
  510.       1      -00045.8600 (minus sign is ignored)
  511.       2      - 0045.8600
  512.       3      -  045.8600
  513.       4      -   45.8600
  514.     =====================
  515.  
  516. If all characters before the decimal point are zeros:
  517.  
  518.     000000.0432
  519.  
  520. the routine will strip the zeros until it reaches the decimal point,
  521. resulting in:
  522.  
  523.     .0432
  524.  
  525. If you wish to retain one leading zero, simply change the code at LDSTP2 to
  526. this:
  527.  
  528.     LDSTP2   INY            ;BUMP OFFSET
  529.              CPY #$05
  530.              BNE LDSTP1     ;LOOP
  531.                             ;
  532.              (program continues...)
  533.  
  534. This would format:
  535.  
  536.     000000.0432
  537.  
  538. to:
  539.  
  540.     0.0432
  541. a format style often desired in scientific and engineering calculations.
  542.  
  543. The preceding routine stripped the leading zeros from the ASCII string.
  544. You may wish to strip trailing zeros as well.  As with the previous
  545. technique, the routine stops upon reaching the first non-zero character in
  546. the string.  In this case however, the routine starts from the tenth
  547. character position in ACUMA and works in reverse.
  548.  
  549. Here is a routine to replace trailing zeros with blanks:
  550.  
  551.     TRSTP    LDX #$20       ;ASCII BLANK
  552.              LDY #$0A       ;OFFSET
  553.                             ;
  554.     TRSTP1   LDA ACUMA,Y    ;FETCH CHARACTER
  555.              CMP #$30       ;ASCII ZERO
  556.              BNE TRSTP2     ;TERMINATE LOOP
  557.                             ;
  558.              TXA
  559.              STA ACUMA,Y    ;SUBSTITUTE BLANK
  560.                             ;
  561.              DEY            ;LOWER OFFSET
  562.              BNE TRSTP1     ;LOOP
  563.                             ;
  564.     TRSTP2   CMP #$2E       ;DECIMAL POINT?
  565.              BNE TRSTPO     ;NO, IGNORE CHARACTER
  566.                             ;
  567.              TXA
  568.              STA ACUMA,Y    ;REMOVE DECIMAL POINT
  569.                             ;
  570.     TRSTPO   RTS            ;EXIT
  571.  
  572. The effect of this and the previous routine for stripping leading zeros is
  573. again best understood by looking at some strings before and after
  574. formatting:
  575.  
  576.     UNFORMATTED     LEADING STRIP     TRAILING STRIP
  577.     ================================================
  578.     006492.8900       6492.8900         6492.89
  579.     172492.0000     172492.0000       172492
  580.     000000.0000           .0000
  581.     000000.0123           .0123             .0123
  582.     -00076.1030     -   76.1030       -   76.103
  583.     ================================================
  584.  
  585. About this time you are probably wondering what happened to the string in
  586. the third example.  No, it isn't a typographical error.  Stripping all of
  587. the leading zeros and stripping all of the trailing zeros results in a
  588. completely blank string (all ASCII 32).  Naturally, that isn't desirable.
  589. For that reason it is best to use the modified version of LDSTP (see
  590. above), which always leaves a zero immediately before the decimal point.
  591. Thus:
  592.  
  593.     000000.0000
  594.  
  595. formats to:
  596.  
  597.     0
  598.  
  599. a much more meaningful result.  With the concept of stripping leading and
  600. trailing zeros in mind, let's go on to alternate types of formatting.
  601.  
  602. The effect of stripping trailing zeros in the above routine is to replace
  603. them with blanks.  The result of this is that when combined with the
  604. stripping of leading zeros the string remains the same length as before
  605. stripping.  Obviously this is very handy when printing columns of numbers
  606. as it makes it easy to format the display itself (the end of one string and
  607. the beginning of the next are always a fixed distance from each other).
  608. Certain displays however, may require that the numbers not be justified.
  609. For example, you may wish to print:
  610.  
  611.     ACCOUNT BALANCE $1867.29 ON 11-16-85
  612.  
  613. To achieve such a display, leading zeros must be stripped and the string
  614. must then be dejustified.  Also, two characters to the right of the decimal
  615. point should be retained so that even dollar amounts don't end up with a
  616. strange appearance.  The first step after stripping the leading zeros is to
  617. insert a new terminator ($00) into ACUMA so as to leave two characters to
  618. the right of the decimal point.  Since the decimal point is known to be in
  619. the seventh character position it can be deduced that the new terminator
  620. should be stored at the tenth character position:
  621.  
  622.              LDA #$00       ;TERMINATOR
  623.              STA ACUMA+9    ;TRUNCATE STRING
  624.  
  625. Next, the string must be shifted left in ACUMA to eliminate the blanks that
  626. were used to replace the leading zeros during the stripping process.  This
  627. is accomplished by using two offsets, one to locate the non-blank
  628. characters and the other to relocate the characters closer to the beginning
  629. of the accumulator.  The resulting loop terminates when the terminator null
  630. is located.  Here is an algorithm to dejustify a string in ACUMA:
  631.  
  632.     DJUST    LDX #$00       ;FETCH OFFSET
  633.              LDY #$00       ;STORAGE OFFSET
  634.                             ;
  635.     DJUST1   LDA ACUMA,X    ;FETCH CHARACTER
  636.              BEQ DJUSTO     ;END OF STRING
  637.                             ;
  638.              CMP #$20       ;ASCII BLANK
  639.              BEQ DJUST2     ;DISCARD
  640.                             ;
  641.              STA ACUMA,Y    ;STORE NON-BLANK CHARACTER
  642.              INY            ;BUMP STORAGE OFFSET
  643.                             ;
  644.     DJUST2   INX            ;BUMP FETCH OFFSET
  645.              BNE DJUST1     ;LOOP
  646.                             ;
  647.     DJUSTO   STA ACUMA,Y    ;TERMINATOR
  648.                             ;
  649.              RTS            ;EXIT
  650.  
  651. The number of iterations required by this routine will depend upon the
  652. terminator position in ACUMA.  Here is the effect upon a stripped string in
  653. ACUMA during each pass through the routine:
  654.  
  655.     ITERATION    ACUMA
  656.     ===================
  657.     Start    -   62.87*
  658.       1      -   62.87* (skip minus sign)
  659.       2      -   62.87* (skip blank)
  660.       3      -   62.87* (skip blank)
  661.       4      -   62.87* (skip blank)
  662.       5      -6  62.87*
  663.       6      -62 62.87*
  664.       7      -62.62.87*
  665.       8      -62.82.87*
  666.       9      -62.87.87*
  667.      10      -62.87*87*
  668.     ===================
  669.  
  670. The terminator is represented by the asterisk.  Note that at the end of the
  671. tenth iteration the terminator has been moved from the tenth position to
  672. the seventh position in ACUMA.  The result of this is that characters after
  673. the seventh position will be ignored in subsequent operations.
  674.  
  675. IV.  RECAP
  676.  
  677. The preceding chapters have described techniques for converting ASCII to
  678. BCD and vice versa, building upon the knowledge gained in THE ABC'S OF BCD
  679. part 1.  Prior to continuing to part 3, code in the example routines and
  680. run them to see what happens.  Try overflowing the ASCII accumulator to see
  681. what result it has upon the equivalent BCD number.  If you are using a
  682. C-128 the memory area in RAM 0 from $0C00 to $0DFF is a good place to
  683. practice as it is undisturbed (unless using the RS-232 routines).
  684.  
  685. In THE ABC'S OF BCD part 3, the subject of transferring BCD numbers from
  686. one location to another and the techniques of signed addition and
  687. subtraction will be discussed.  Here you will learn how to perform math
  688. operations upon your BCD numbers.  When combined with the conversion and
  689. formatting techniques described above, you will have the makings of a
  690. machine language math package based entirely on the decimal numbering
  691. system.
  692.